package pl.jtb.ctl.tabory.dao.impl.mybatis.impl;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.log4j.Logger;

import pl.jtb.ctl.tabory.dao.api.services.IStationService;
import pl.jtb.ctl.tabory.dao.data.vo.StationVO;
import pl.jtb.ctl.tabory.dao.impl.mybatis.base.BaseDaoService;

public class StationSerivceImpl extends BaseDaoService<StationVO> implements IStationService {

    private final static Logger logger = Logger.getLogger(StationSerivceImpl.class);

    private final static String MAPPER = "pl.jtb.ctl.taboty.StationMapper";
    private final static String SQL_SELECT_ALL = ".selectAllStations";
    private final static String SQL_SAVE = ".saveStation";
    private final static String SQL_SAVE_DISTANCES = ".saveStationValue";
    private final static String SQL_SELECT_BY_NAME = ".selectStationByName";
    private final static String SQL_SELECT_DISTANCE_BY_STATIOIN = ".selectDistanceForStation";
    private final static String SQL_SELECT_BY_ID = ".selectStationById";
    private final static String SQL_UPDATE = ".updateStation";
    private final static String SQL_DELETE = ".deleteStation";
    private final static String SQL_DELETE_DISTANCES = ".deleteStationValues";

    private final static String COL_STATION_START = "STATION_START_ID";
    private final static String COL_STATION_STOP = "STATION_STOP_ID";
    private final static String COL_STATION_DISTANCE = "DISTANCE";

    public StationSerivceImpl(SqlSessionFactory sqlSessionFactory) {
        super(sqlSessionFactory);
    }

    @Override
    public List<StationVO> getAllStations() throws Exception {
        logger.debug("Getting all Stations");
        List<StationVO> result = null;
        try {
            result = selectList(MAPPER + SQL_SELECT_ALL, null);
        } catch (Exception e) {
            result = new ArrayList<StationVO>();
            logger.error(e.getMessage(), e);
            throw e;
        }
        return result;
    }

    @Override
    public StationVO getStation(String stationName) throws Exception {
        logger.debug("Getting Station by name");
        StationVO result = null;
        try {
            result = selectOne(MAPPER + SQL_SELECT_BY_NAME, stationName);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw e;
        }
        return result;
    }

    @Override
    public StationVO getStation(long id) throws Exception {
        logger.debug("Getting Station by ID");
        StationVO result = null;
        try {
            result = selectOne(MAPPER + SQL_SELECT_BY_ID, id);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw e;
        }
        return result;
    }

    @Override
    public StationVO saveUpdateStation(StationVO station) throws Exception {
        logger.debug("Save/Update Station: " + station);
        station.setActive(true);
        if (station.getId() == null) {
            saveStation(station);
        } else {
            updateStation(station);
        }
        return null;
    }

    private void saveStation(StationVO station) throws Exception {
        logger.debug("Saving new Station: " + station);
        try {
            create(MAPPER + SQL_SAVE, station);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw e;
        }
    }

    private void updateStation(StationVO station) throws Exception {
        logger.debug("Updating existing Station: " + station);
        try {
            create(MAPPER + SQL_UPDATE, station);
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw e;
        }
    }

    @Override
    public void delecteStation(StationVO station) throws Exception {
        logger.debug("Deleting Station: " + station);
        try {
            delete(MAPPER + SQL_DELETE, station.getId());
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw e;
        }
    }

    @Override
    @SuppressWarnings("unchecked")
    public Map<StationVO, Double> getDistanceMap(StationVO stationVO) throws Exception {
        Map<StationVO, Double> result = new HashMap<StationVO, Double>();
        SqlSession session = null;
        try {
            session = sessionFactory.openSession();
            List<Map<String, Object>> selectList = (List<Map<String, Object>>) session.selectList(MAPPER + SQL_SELECT_DISTANCE_BY_STATIOIN, stationVO.getId());
            for (Map<String, Object> map : selectList) {
                StationVO station = getStation((Long) map.get(COL_STATION_STOP));
                result.put(station, (Double) map.get(COL_STATION_DISTANCE));
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw e;
        } finally {
            session.close();
        }
        return result;
    }

    @Override
    public Map<StationVO, Map<StationVO, Double>> getAllDistances() throws Exception {
        List<StationVO> allStations = getAllStations();
        Map<StationVO, Map<StationVO, Double>> result = new HashMap<StationVO, Map<StationVO, Double>>();
        for (StationVO stationVO : allStations) {
            Map<StationVO, Double> distanceMap = getDistanceMap(stationVO);
            result.put(stationVO, distanceMap);
        }
        return result;
    }

    @Override
    public void saveDistances(Map<StationVO, Map<StationVO, Double>> mapDistances) throws Exception {
        SqlSession session = null;
        try {
            deleteDistances();
            session = sessionFactory.openSession();
            for (Entry<StationVO, Map<StationVO, Double>> entry : mapDistances.entrySet()) {
                StationVO stationFrom = entry.getKey();
                for (Entry<StationVO, Double> distanceEntry : entry.getValue().entrySet()) {
                    StationVO stationTo = distanceEntry.getKey();
                    Double distance = distanceEntry.getValue();
                    Map<String, Object> parameterMap = new HashMap<String, Object>();
                    parameterMap.put("stationStartId", stationFrom.getId());
                    parameterMap.put("stationStopId", stationTo.getId());
                    parameterMap.put("distance", distance);
                    session.insert(MAPPER + SQL_SAVE_DISTANCES, parameterMap);
                    session.commit();
                }
            }
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw e;
        } finally {
            session.close();
        }
    }

    @Override
    public void deleteDistances() throws Exception {
        SqlSession session = null;
        try {
            session = sessionFactory.openSession();
            session.delete(MAPPER + SQL_DELETE_DISTANCES);
            session.commit();
        } catch (Exception e) {
            logger.error(e.getMessage(), e);
            throw e;
        } finally {
            session.close();
        }
    }

}
